#ifndef U2Bitwise_H
#define U2Bitwise_H

#include "U2PreRequest.h"


U2EG_NAMESPACE_BEGIN


class _U2Share U2Bitwise
{
public:

    /** Returns the closest power-of-two number greater or equal to value.
    @note 0 and 1 are powers of two, so 
        firstPO2From(0)==0 and firstPO2From(1)==1.
    */
    static FORCEINLINE u2uint32 firstPO2From(u2uint32 n)
    {
        --n;            
        n |= n >> 16;
        n |= n >> 8;
        n |= n >> 4;
        n |= n >> 2;
        n |= n >> 1;
        ++n;
        return n;
    }

	template<typename T>
	static FORCEINLINE bool isPO2(T n)
	{
		return (n & (n-1)) == 0;
	}

    /** Returns the number of bits a pattern must be shifted right by to
        remove right-hand zeros.
    */
	template<typename T>
    static FORCEINLINE unsigned int getBitShift(T mask)
	{
		if (mask == 0)
			return 0;

		unsigned int result = 0;
		while ((mask & 1) == 0) {
			++result;
			mask >>= 1;
		}
		return result;
	}

    /**
     * Convert N bit colour channel value to P bits. It fills P bits with the
     * bit pattern repeated. (this is /((1<<n)-1) in fixed point)
     */
    static inline unsigned int fixedToFixed(u2uint32 value, unsigned int n, unsigned int p) 
    {
        if(n > p) 
        {
            // Less bits required than available; this is easy
            value >>= n-p;
        } 
        else if(n < p)
        {
            // More bits required than are there, do the fill
            // Use old fashioned division, probably better than a loop
            if(value == 0)
                    value = 0;
            else if(value == (static_cast<unsigned int>(1)<<n)-1)
                    value = (1<<p)-1;
            else    value = value*(1<<p)/((1<<n)-1);
        }
        return value;    
    }

    /**
     * Convert floating point colour channel value between 0.0 and 1.0 (otherwise clamped) 
     * to integer of a certain number of bits. Works for any value of bits between 0 and 31.
     */
    static inline unsigned int floatToFixed(const float value, const unsigned int bits)
    {
        if(value <= 0.0f) return 0;
        else if (value >= 1.0f) return (1<<bits)-1;
        else return (unsigned int)(value * (1<<bits));     
    }

    /**
     * Fixed point to float
     */
    static inline float fixedToFloat(unsigned value, unsigned int bits)
    {
        return (float)value/(float)((1<<bits)-1);
    }

    /**
     * Write a n*8 bits integer value to memory in native endian.
     */
    static inline void intWrite(void *dest, const int n, const unsigned int value)
    {
        switch(n) {
            case 1:
                ((u2uint8*)dest)[0] = (u2uint8)value;
                break;
            case 2:
                ((u2uint16*)dest)[0] = (u2uint16)value;
                break;
            case 3:
#if U2_ENDIAN == U2_ENDIAN_BIG      
                ((u2uint8*)dest)[0] = (u2uint8)((value >> 16) & 0xFF);
                ((u2uint8*)dest)[1] = (u2uint8)((value >> 8) & 0xFF);
                ((u2uint8*)dest)[2] = (u2uint8)(value & 0xFF);
#else
                ((u2uint8*)dest)[2] = (u2uint8)((value >> 16) & 0xFF);
                ((u2uint8*)dest)[1] = (u2uint8)((value >> 8) & 0xFF);
                ((u2uint8*)dest)[0] = (u2uint8)(value & 0xFF);
#endif
                break;
            case 4:
                ((u2uint32*)dest)[0] = (u2uint32)value;                
                break;                
        }        
    }
    /**
     * Read a n*8 bits integer value to memory in native endian.
     */
    static inline unsigned int intRead(const void *src, int n) {
        switch(n) {
            case 1:
                return ((u2uint8*)src)[0];
            case 2:
                return ((u2uint16*)src)[0];
            case 3:
#if U2_ENDIAN == U2_ENDIAN_BIG      
                return ((u2uint32)((u2uint8*)src)[0]<<16)|
                        ((u2uint32)((u2uint8*)src)[1]<<8)|
                        ((u2uint32)((u2uint8*)src)[2]);
#else
                return ((u2uint32)((u2uint8*)src)[0])|
                        ((u2uint32)((u2uint8*)src)[1]<<8)|
                        ((u2uint32)((u2uint8*)src)[2]<<16);
#endif
            case 4:
                return ((u2uint32*)src)[0];
        } 
        return 0; // ?
    }

    /** Convert a float32 to a float16 (NV_half_float)
     	Courtesy of OpenEXR
    */
    static inline u2uint16 floatToHalf(float i)
    {
        union { float f; u2uint32 i; } v;
        v.f = i;
        return floatToHalfI(v.i);
    }

	/** Converts float in u2uint32 format to a a half in u2uint16 format
	*/
    static inline u2uint16 floatToHalfI(u2uint32 i)
    {
        register int s =  (i >> 16) & 0x00008000;
        register int e = ((i >> 23) & 0x000000ff) - (127 - 15);
        register int m =   i        & 0x007fffff;
    
        if (e <= 0)
        {
            if (e < -10)
            {
                return 0;
            }
            m = (m | 0x00800000) >> (1 - e);
    
            return s | (m >> 13);
        }
        else if (e == 0xff - (127 - 15))
        {
            if (m == 0) // Inf
            {
                return s | 0x7c00;
            } 
            else    // NAN
            {
                m >>= 13;
                return s | 0x7c00 | m | (m == 0);
            }
        }
        else
        {
            if (e > 30) // Overflow
            {
                return s | 0x7c00;
            }
    
            return s | (e << 10) | (m >> 13);
        }
    }
    
    /**
     * Convert a float16 (NV_half_float) to a float32
     * Courtesy of OpenEXR
     */
    static inline float halfToFloat(u2uint16 y)
    {
        union { float f; u2uint32 i; } v;
        v.i = halfToFloatI(y);
        return v.f;
    }
	/** Converts a half in u2uint16 format to a float
	 	in u2uint32 format
	 */
    static inline u2uint32 halfToFloatI(u2uint16 y)
    {
        register int s = (y >> 15) & 0x00000001;
        register int e = (y >> 10) & 0x0000001f;
        register int m =  y        & 0x000003ff;
    
        if (e == 0)
        {
            if (m == 0) // Plus or minus zero
            {
                return s << 31;
            }
            else // Denormalized number -- renormalize it
            {
                while (!(m & 0x00000400))
                {
                    m <<= 1;
                    e -=  1;
                }
    
                e += 1;
                m &= ~0x00000400;
            }
        }
        else if (e == 31)
        {
            if (m == 0) // Inf
            {
                return (s << 31) | 0x7f800000;
            }
            else // NaN
            {
                return (s << 31) | 0x7f800000 | (m << 13);
            }
        }
    
        e = e + (127 - 15);
        m = m << 13;
    
        return (s << 31) | (e << 23) | m;
    }

};


U2EG_NAMESPACE_END

#endif